package wint.dal.jdbc;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;

import wint.dal.context.DalContext;
import wint.dal.jdbc.util.SqlUtil;
import wint.dal.rule.RuleResult;

public class LazyStatement implements Statement {
	
	private Statement targetStatement;
	
	private DalContext dalContext;
	
	private LazyConnection lazyConnection;
	
	private StatementProperties statementProperties = new StatementProperties();
	
	public LazyStatement(DalContext dalContext, LazyConnection lazyConnection, Integer resultSetType, Integer resultSetConcurrency, Integer resultSetHoldability) {
		super();
		this.dalContext = dalContext;
		this.lazyConnection = lazyConnection;
		statementProperties.setResultSetType(resultSetType);
		statementProperties.setResultSetConcurrency(resultSetConcurrency);
		statementProperties.setResultSetHoldability(resultSetHoldability);
	}

	protected Statement createNewStatement(LazyConnection lazyConnection) throws SQLException {
		Integer resultSetType = statementProperties.getResultSetType();
		Integer resultSetConcurrency = statementProperties.getResultSetConcurrency();
		Integer resultSetHoldability = statementProperties.getResultSetHoldability();
		Statement stmt = lazyConnection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
		Boolean escapeProcessing = statementProperties.getEscapeProcessing();
		if (escapeProcessing != null) {
			stmt.setEscapeProcessing(escapeProcessing);
		}
		Integer fetchSize = statementProperties.getFetchSize();
		if (fetchSize != null) {
			stmt.setFetchSize(fetchSize);
		}
		Integer maxFieldSize = statementProperties.getMaxFieldSize();
		if (maxFieldSize != null) {
			stmt.setMaxFieldSize(maxFieldSize);
		}
		Integer maxRows = statementProperties.getMaxRows();
		if (maxRows != null) {
			stmt.setMaxRows(maxRows);
		}
		Boolean poolable = statementProperties.getPoolable();
		if (poolable != null) {
			stmt.setPoolable(poolable);
		}
		Integer queryTimeout = statementProperties.getQueryTimeout();
		if (queryTimeout != null) {
			stmt.setQueryTimeout(queryTimeout);
		}
		Integer fetchDirection = statementProperties.getFetchDirection();
		if (fetchDirection != null) {
			stmt.setFetchDirection(fetchDirection);
		}
		return stmt;
	}
	
	protected synchronized Statement getTargetStatement() {
		return targetStatement;
	}

	protected void wakeupStatement(String sql) throws SQLException {
		RuleResult ruleResult = dalContext.getRuleEvaluator().eval(sql);
		
		synchronized (this) {
			if (lazyConnection == null) {
				// 新创建
				lazyConnection = dalContext.getConnection(ruleResult);
				targetStatement = lazyConnection.createStatement();
			} else if (ruleResult.isNeedWrite() && !lazyConnection.isSupportWrite()) {
				// 如果原来是只读的，但是现在需要一个写的，需要重新创建一个可写的
				// 并把当期的sql关闭
				LazyConnection supportWriteConnection = dalContext.getConnection(ruleResult);
				
				LazyConnection oldLazyConnection = lazyConnection;
				Statement oldStatement = targetStatement;
				
				lazyConnection = supportWriteConnection;
				targetStatement = createNewStatement(lazyConnection);
				
				SqlUtil.close(oldStatement);
				SqlUtil.close(oldLazyConnection);
			}
		}
	}
	
	public ResultSet executeQuery(String sql) throws SQLException {
		wakeupStatement(sql);
		return getTargetStatement().executeQuery(sql);
	}

	public <T> T unwrap(Class<T> iface) throws SQLException {
		return getTargetStatement().unwrap(iface);
	}

	public int executeUpdate(String sql) throws SQLException {
		wakeupStatement(sql);
		return getTargetStatement().executeUpdate(sql);
	}

	public boolean isWrapperFor(Class<?> iface) throws SQLException {
		return getTargetStatement().isWrapperFor(iface);
	}

	public void close() throws SQLException {
		getTargetStatement().close();
	}

	public int getMaxFieldSize() throws SQLException {
		return getTargetStatement().getMaxFieldSize();
	}

	public void setMaxFieldSize(int max) throws SQLException {
		statementProperties.setMaxFieldSize(max);
		getTargetStatement().setMaxFieldSize(max);
	}

	public int getMaxRows() throws SQLException {
		return getTargetStatement().getMaxRows();
	}

	public void setMaxRows(int max) throws SQLException {
		statementProperties.setMaxRows(max);
		getTargetStatement().setMaxRows(max);
	}

	public void setEscapeProcessing(boolean enable) throws SQLException {
		statementProperties.setEscapeProcessing(enable);
		getTargetStatement().setEscapeProcessing(enable);
	}

	public int getQueryTimeout() throws SQLException {
		return getTargetStatement().getQueryTimeout();
	}

	public void setQueryTimeout(int seconds) throws SQLException {
		statementProperties.setQueryTimeout(seconds);
		getTargetStatement().setQueryTimeout(seconds);
	}

	public void cancel() throws SQLException {
		getTargetStatement().cancel();
	}

	public SQLWarning getWarnings() throws SQLException {
		return getTargetStatement().getWarnings();
	}

	public void clearWarnings() throws SQLException {
		getTargetStatement().clearWarnings();
	}

	public void setCursorName(String name) throws SQLException {
		getTargetStatement().setCursorName(name);
	}

	public boolean execute(String sql) throws SQLException {
		wakeupStatement(sql);
		return getTargetStatement().execute(sql);
	}

	public ResultSet getResultSet() throws SQLException {
		return getTargetStatement().getResultSet();
	}

	public int getUpdateCount() throws SQLException {
		return getTargetStatement().getUpdateCount();
	}

	public boolean getMoreResults() throws SQLException {
		return getTargetStatement().getMoreResults();
	}

	public void setFetchDirection(int direction) throws SQLException {
		getTargetStatement().setFetchDirection(direction);
	}

	public int getFetchDirection() throws SQLException {
		return getTargetStatement().getFetchDirection();
	}

	public void setFetchSize(int rows) throws SQLException {
		statementProperties.setFetchSize(rows);
		getTargetStatement().setFetchSize(rows);
	}

	public int getFetchSize() throws SQLException {
		return getTargetStatement().getFetchSize();
	}

	public int getResultSetConcurrency() throws SQLException {
		return getTargetStatement().getResultSetConcurrency();
	}

	public int getResultSetType() throws SQLException {
		return getTargetStatement().getResultSetType();
	}

	public void addBatch(String sql) throws SQLException {
		// TODO 这个需要支持多个connection
		//getTargetStatement().addBatch(sql);
		throw new UnsupportedOperationException();
	}

	public void clearBatch() throws SQLException {
		//getTargetStatement().clearBatch();
		throw new UnsupportedOperationException();
	}

	public int[] executeBatch() throws SQLException {
		// TODO
		//return getTargetStatement().executeBatch();
		throw new UnsupportedOperationException();
	}

	public Connection getConnection() throws SQLException {
		return getTargetStatement().getConnection();
	}

	public boolean getMoreResults(int current) throws SQLException {
		return getTargetStatement().getMoreResults(current);
	}

	public ResultSet getGeneratedKeys() throws SQLException {
		//return getTargetStatement().getGeneratedKeys();
		throw new UnsupportedOperationException();
	}

	public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
		wakeupStatement(sql);
		return getTargetStatement().executeUpdate(sql, autoGeneratedKeys);
	}

	public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
		wakeupStatement(sql);
		return getTargetStatement().executeUpdate(sql, columnIndexes);
	}

	public int executeUpdate(String sql, String[] columnNames) throws SQLException {
		wakeupStatement(sql);
		return getTargetStatement().executeUpdate(sql, columnNames);
	}

	public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
		wakeupStatement(sql);
		return getTargetStatement().execute(sql, autoGeneratedKeys);
	}

	public boolean execute(String sql, int[] columnIndexes) throws SQLException {
		wakeupStatement(sql);
		return getTargetStatement().execute(sql, columnIndexes);
	}

	public boolean execute(String sql, String[] columnNames) throws SQLException {
		wakeupStatement(sql);
		return getTargetStatement().execute(sql, columnNames);
	}

	public int getResultSetHoldability() throws SQLException {
		return getTargetStatement().getResultSetHoldability();
	}

	public boolean isClosed() throws SQLException {
		return getTargetStatement().isClosed();
	}

	public void setPoolable(boolean poolable) throws SQLException {
		statementProperties.setPoolable(poolable);
		getTargetStatement().setPoolable(poolable);
	}

	public boolean isPoolable() throws SQLException {
		return getTargetStatement().isPoolable();
	}

}
